package it.softphone.cmc.server.service.impl;

import it.softphone.cmc.server.pojosr.GaeClasspathScanner;
import it.softphone.cmc.server.service.BundleService;
import it.softphone.cmc.server.service.exception.CMCException;
import it.softphone.commons.service.LogAware;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

import org.apache.commons.lang.NullArgumentException;
import org.apache.commons.vfs.FileName;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemManager;
import org.apache.commons.vfs.FileType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.newatlanta.commons.vfs.provider.gae.GaeClassLoader;

import de.kalpatec.pojosr.framework.launch.BundleDescriptor;
import de.kalpatec.pojosr.framework.launch.ClasspathScanner;
import de.kalpatec.pojosr.framework.launch.PojoServiceRegistry;
import de.kalpatec.pojosr.framework.launch.PojoServiceRegistryFactory;

@Service
public class GAEBundleServiceImpl implements BundleService,LogAware {

	static class CMCBundle implements Bundle {

		final ClassLoader classLoader;
		final PojoServiceRegistry serviceRegistry;
		final FileName name;
		
		public CMCBundle( FileObject folder, ClassLoader classLoader ) throws Exception 
		{
			this.classLoader = classLoader;
			this.serviceRegistry = newServiceRegistry(classLoader);
			this.name = folder.getName();
		
			
		}

		private final PojoServiceRegistry newServiceRegistry(ClassLoader classLoader) throws Exception {
			
			ClasspathScanner cps = new GaeClasspathScanner();
			
			java.util.List<BundleDescriptor> bundles = cps.scanForBundles(classLoader);
			
			for( BundleDescriptor bd : bundles ) {
				
				logger.debug(String.format( "bundle [%s]", bd.toString()) );
			}
			
			
			Map<String,Object> config = new HashMap<String,Object>();
			config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, new ClasspathScanner().scanForBundles());
	
			ServiceLoader<PojoServiceRegistryFactory> loader = ServiceLoader.load(PojoServiceRegistryFactory.class);
	
			PojoServiceRegistry registry = loader.iterator().next().newPojoServiceRegistry(config);
			
			registry.startBundles(bundles);
			
			return registry;

		}
		@Override
		public final String getId() {
			return name.getBaseName();
		}
		
		@Override
		public final ClassLoader getClassLoader() {
			return classLoader;
		}
		
		@Override
		public final PojoServiceRegistry getServiceRegistry() {
			return serviceRegistry;
		}

		@Override
		public String getPath() {
			return name.getPath();
		}
		
		
	}
	
	@Autowired FileSystemManager fileSystemManager;
	
	
	final java.util.Map<String,Bundle> bundleCache = 
			new java.util.HashMap<String,Bundle>(10);
	
	@Override
	public Bundle getBundle(String name) {
		
		return bundleCache.get(name);
	}

	@Override
	public Bundle createBundle(FileObject folder) throws CMCException {
		
		if( folder == null ) {
			throw new NullArgumentException("folder");
		}
		
		try {
			if( !FileType.FOLDER.equals(folder.getType()) ){
				throw new IllegalStateException(String.format("[%s] is not a folder", folder.toString()) );
			}
			
			final String name = folder.getName().getBaseName();
			
			if( bundleCache.containsKey(name) ) {
				throw new IllegalStateException(String.format("bundle [%s] already exist!", name) );
			}
			
			
			FileObject[] children = folder.getChildren();
			
			/*		
					folder.findFiles( new FileSelector() {
				
				@Override
				public boolean traverseDescendents(FileSelectInfo fileInfo)
						throws Exception {
					return fileInfo.getFile().getType().equals(FileType.FOLDER);
				}
				
				@Override
				public boolean includeFile(FileSelectInfo fileInfo) throws Exception {
					
					if( fileInfo.getFile().getType().equals(FileType.FOLDER) ) {
						return false;
					}
					
					final String ext = fileInfo.getFile().getName().getExtension();
					
					return ext.toLowerCase().equals( "jar");			}
			});
			*/
			if( children == null || children.length == 0 ) {
				throw new IllegalStateException(String.format("[%s] has not jar associated", folder.toString()) );			
			}
			
	        for( FileObject fo : children ) {
	        	
	        	logger.debug( String.format("fo [%s]:[%s]\n", fo.getName().getBaseName(), fo.getName().getPath() ));
	        }
			
	        final GaeClassLoader cl =  new GaeClassLoader(children, fileSystemManager, getClass().getClassLoader());
	        Bundle result =  new CMCBundle( folder, cl );
	        
	        bundleCache.put( result.getId(), result);
	        
	        return result;
	        
		} catch (Exception e) {
			throw new CMCException(e);
		}
		

	}

	@Override
	public Collection<Bundle> getBundles() {
		return Collections.unmodifiableCollection(bundleCache.values());
	}

}
